package net.wicp.tams.common.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.druid.pool.DruidDataSource;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.CallbackUpdate;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.SshAssist;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectExceptionRuntime;

@Slf4j
public class DruidAssit {

	private static final java.util.Map<String, DataSource> instmap = new java.util.HashMap<String, DataSource>();

	static {
		Conf.addCallBack(new CallbackUpdate() {
			@Override
			public void doReshConf(Properties oldProperties, Properties newProperties) {
				shutdown();
			}
		}, "common.jdbc.datasource.");
	}

	/***
	 * 数据源关闭
	 */
	public static void shutdown() {
		for (String poolname : instmap.keySet()) {
			((DruidDataSource) instmap.get(poolname)).close();
		}
		instmap.clear();
	}

	/***
	 * 得到所有数据源
	 * 
	 * @return
	 */
	public static java.util.Map<String, DataSource> getExecutorservicemap() {
		return instmap;
	}

	public static final DataSource getDataSource(String name) {
		if (instmap.get(name) == null) {
			synchronized (DruidAssit.class) {
				if (instmap.get(name) == null) {
					Properties props = Conf.getPreToProp("common.jdbc.datasource.default", true);
					if (!"default".equals(name)) {
						Map<String, String> map = Conf.getPre("common.jdbc.datasource." + name, true);
						for (String key : map.keySet()) {
							props.put(key, map.get(key));
						}
					}
					Conf.overProp(props);
					DataSource initDataSource = initDataSource(props);
					instmap.put(name, initDataSource);
				}
			}
		}
		return instmap.get(name);
	}

	/**
	 * 加入一下外部的数据源，不在配置里面，只能执行一次，适合于动态加数据源
	 * 
	 * @param name
	 * @param props
	 * @return
	 */
	public static DataSource getDataSourceNoConf(String name, Properties props) {
		if (instmap.containsKey(name)) {
			log.warn("已存在此配置,可以调用getDataSource拿到数据源");
			return instmap.get(name);
		}
		Properties propsDefault = Conf.getPreToProp("common.jdbc.datasource.default", true);// 拿到默认配置源
		for (Object key : props.keySet()) {
			propsDefault.put(String.valueOf(key), props.get(key));
		}
		Properties overprops = new Properties();
		for (Object keyobj : propsDefault.keySet()) {
			overprops.put(String.format("common.jdbc.datasource.%s.%s", name, keyobj), propsDefault.get(keyobj));
		}
		// 先放到内存中
		Conf.overProp(overprops);
		return getDataSource(name);
	}

	public static boolean existDataSource(String name) {
		return instmap.containsKey(name);
	}

	public static final DataSource getDataSource() {
		return getDataSource("default");
	}

	public static Connection getConnection(String name) {
		try {
			DataSource dataSource = getDataSource(name);
			return dataSource.getConnection();
		} catch (SQLException e) {
			log.error("获得连接失败");
			throw new ProjectExceptionRuntime(ExceptAll.jdbc_conn_fail, e);
		}
	}

	public static Connection getConnection() {
		return getConnection("default");
	}

	private static DataSource initDataSource(Properties props) {
		String temp = "jdbc:mysql://%s:%s";
		String url;
		if (StringUtil.isNotNull(props.get("sshLocalhostprot"))) {
			SshAssist.ssh(Integer.parseInt(get(props, "sshLocalhostprot")), get(props, "host"),
					Integer.parseInt(get(props, "port")));
			url = String.format(temp, "localhost", get(props, "sshLocalhostprot"));
		} else {
			url = String.format(temp, get(props, "host"), get(props, "port"));
		}
		if (StringUtil.isNotNull(get(props, "defaultdb"))) {
			url += "/" + get(props, "defaultdb");
		}
		// urlparam的默认值不能放配置文件，编译出错
		url += "?" + StringUtil.hasNull(get(props, "urlparam"),
				"useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false");
		DruidDataSource datasource = new DruidDataSource();
		datasource.setUrl(url);
		datasource.setUsername(get(props, "username"));
		datasource.setPassword(get(props, "password"));
		datasource.setDriverClassName(get(props, "driverClassName"));
		datasource.setDbType(get(props, "dbType"));

		// configuration
		datasource.setInitialSize(Integer.parseInt(get(props, "initialSize")));
		datasource.setMinIdle(Integer.parseInt(get(props, "minIdle")));
		datasource.setMaxActive(Integer.parseInt(get(props, "maxActive")));
		datasource.setMaxWait(Integer.parseInt(get(props, "maxWait")));

		datasource.setTimeBetweenEvictionRunsMillis(Integer.parseInt(get(props, "timeBetweenEvictionRunsMillis")));
		datasource.setMinEvictableIdleTimeMillis(Integer.parseInt(get(props, "minEvictableIdleTimeMillis")));
		datasource.setValidationQuery(get(props, "validationQuery"));
		datasource.setTestWhileIdle(Boolean.parseBoolean(get(props, "testWhileIdle")));
		datasource.setTestOnBorrow(Boolean.parseBoolean(get(props, "testOnBorrow")));
		datasource.setTestOnReturn(Boolean.parseBoolean(get(props, "testOnReturn")));
		datasource.setPoolPreparedStatements(Boolean.parseBoolean(get(props, "poolPreparedStatements")));
		datasource.setMaxPoolPreparedStatementPerConnectionSize(
				Integer.parseInt(get(props, "maxPoolPreparedStatementPerConnectionSize")));
		String filters = get(props, "filters");
		if (StringUtils.isNotBlank(filters)) {
			try {
				datasource.setFilters(filters);
			} catch (SQLException e) {
				log.error("druid configuration initialization filter", e);
			}
		}
		String connectionProperties = get(props, "connectionProperties");
		if (StringUtils.isNotBlank(connectionProperties)) {
			datasource.setConnectionProperties(connectionProperties);
		}
		return datasource;
	}

	private static String get(Properties props, String key) {
		return props.get(key) == null ? null : String.valueOf(props.get(key));
	}
}
